#include <iostream>
#include <queue>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>

using namespace std;

static int counter = 0;
const unsigned int usleep_time = 1000;

/*Four functions, which will be executed by threads in thread-pool*/
void *func0(void* arg)
{
    int num = *((int*)arg);
    printf("%s %d: working, arg is %d\n", __func__, __LINE__, num);
    /*Here sleep for a moment, we can see the process of thread execution more clearly*/
    usleep(usleep_time);
    return nullptr;
}

void *func1(void* arg)
{
    int num = *((int*)arg);
    printf("%s %d: working, arg is %d\n", __func__, __LINE__, num);
    usleep(usleep_time);
    return nullptr;
}

void *func2(void* arg)
{
    int num = *((int*)arg);
    printf("%s %d: working, arg is %d\n", __func__, __LINE__, num);
    usleep(usleep_time);
    return nullptr;
}

void *func3(void* arg)
{
    int num = *((int*)arg);
    printf("%s %d: working, arg is %d\n", __func__, __LINE__, num);
    usleep(usleep_time);
    return nullptr;
}

/*Make the function-pointer a alias work_func*/
using work_func =  void *(*)(void *);

class thread_pool_task {
public:
    /*Overloading constructor*/
    thread_pool_task():function(nullptr), arg(nullptr){}
    thread_pool_task(work_func func, void *argu)  {
        this->function = func;
        this->arg = argu;
    }
    /*Copy constructor*/
    thread_pool_task(const thread_pool_task &obj) {
      this->function = obj.function;
      this->arg = obj.arg;
    }
    ~thread_pool_task(){}
    void set_task_arg(work_func func, void *argu) {
        this->function = func;
        this->arg = argu;
    }
    void task_exec() {
        this->function(this->arg);
    }
    void task_arg_delete() {
        int *free_arg = static_cast<int *>(arg);
        delete free_arg;
        arg = nullptr;
    }
    bool task_param_check();

private:
    work_func function;
    void* arg;
};

/*Before execute one task, had better chech its parameter*/
bool thread_pool_task::task_param_check()
{
  if (this->function == nullptr || this->arg == nullptr)  {
      return false;
  }
  return true;
}

class thread_task_list {
public:
  thread_task_list(size_t max_num) {
      pthread_mutex_init(&this->thread_task_list_mutex_lock, nullptr);
      this->list_max_num = max_num;
  }
  ~thread_task_list() {
      pthread_mutex_destroy(&this->thread_task_list_mutex_lock);
  }

  /*Do need to lock here*/
  size_t get_task_list_cur_size() {
      return this->task_list.size();
  }

  size_t get_task_list_max_size() {
      return this->list_max_num;
  }

  bool task_add(work_func, void*);
  thread_pool_task *task_get();

private:
    pthread_mutex_t thread_task_list_mutex_lock;
    size_t list_max_num;
    /*Use std queue to store tasks*/
    queue<thread_pool_task> task_list;
}; 

bool thread_task_list::task_add(work_func func, void *arg)
{
    bool ret = false;
    thread_pool_task *task = nullptr;
    /* If fail to new for task, it will throw a exception,
     * use try-catch to process this exception.
     */
    try {
      /* Here, had better use heap memory, rather than stack memory.
       * If this function return, stack memory may be recycled.
       */
      task = new class thread_pool_task(func, arg);     
    } catch (std::bad_alloc &e) {
        cout << "Fail to new for task, error info: " << e.what() << endl;
    }
    pthread_mutex_lock(&this->thread_task_list_mutex_lock) ;
    if (task_list.size() < list_max_num) {
        task_list.push(*task);
        ret = true;
    }
    pthread_mutex_unlock(&this->thread_task_list_mutex_lock) ;
    return ret;
}

thread_pool_task *thread_task_list::task_get()
{
    thread_pool_task *get_task = nullptr;
    pthread_mutex_lock(&this->thread_task_list_mutex_lock) ;
    if (!task_list.empty()) {
        get_task = &task_list.front();
        /* Do not forget to pop here, because front() just gets, 
         * does not pop
         */
        task_list.pop();
    }
    pthread_mutex_unlock(&this->thread_task_list_mutex_lock) ;
    return get_task;
}

class worker_thread {
public:
    worker_thread(): is_use(0), tid(0){};
    ~worker_thread(){};

    pthread_t get_thread_tid() {
        return this->tid;
    }
    pthread_t *get_thread_tid_addr() {
        return &this->tid;
    }
    void set_thread_tid(pthread_t thread_id) {
        this->tid = thread_id;
    }
    int8_t get_use_status() {
        return this->is_use;
    }
    void set_use_status(int8_t use_status) {
        this->is_use = use_status;
    }
private:
    /*
     * If is_use !=0 and tid != 0, it means that this thread ocupies.
     * If is_use == 0, and tid != 0, manager should recycle the son thread resource.
     * If is_use ==0, and tid ==0, it means that this node is really not used.
     */

    /* The reason why design like above, is that when a thread exit, 
     * father thread must recycle its resource, so we use is_use and tid here,
     * to record current thread status. 
     * If current thread exit, set is_use as zero, when manager thread wants to add
     * a new son thread, it will recycle exited son thread resource passingly.
     */
    int8_t is_use;
    pthread_t tid;
};

class thread_pool;
void *thread_manager(void *);
void *thread_worker(void *);
int work_thread_add(thread_pool* thread_pool, size_t add_num);
worker_thread* find_thread_in_work_thread_list(worker_thread*, int, pthread_t);
bool thread_task_add(thread_pool* thread_pool, int num);
int thread_pool_destroy(thread_pool* thread_pool);

class thread_pool: public thread_task_list
{
public:
    thread_pool(size_t min_thread_num, size_t max_thread_num, size_t list_max_num);
    worker_thread *get_thread_pool_work_threads() {
        return work_thread;
    }
    pthread_mutex_t *get_thread_pool_mutex() {
        return &this->thread_pool_mutex;
    }
    pthread_mutex_t *get_thread_pool_busy_thread_mutex() {
        return &this->busy_thread_mutex;
    }
    worker_thread *get_worker_thread() {
        return this->work_thread;
    }
    pthread_t get_manager_id() {
        return this->manager_id;
    }
    /*If num == 0, it actually means to get*/
    size_t get_or_add_finish_task_num (size_t num) {
        return (this->have_finish_work_num += num);
    }
    size_t get_or_add_fail_task_num (size_t num) {
        return (this->have_fail_work_num += num);
    }
    size_t get_or_add_live_thread_num(size_t num) {
        return (this->live_thread_num += num);
    }
    size_t get_or_add_exit_thread_num(size_t num) {
        return (this->exit_thread_num += num);
    }
    size_t get_or_add_busy_thread_num(size_t num) {
        return (this->busy_thread_num += num);
    }
    size_t get_min_thread_num() {
        return this->min_thread_num; 
    }
    size_t get_max_thread_num() {
          return this->max_thread_num;
    }
    pthread_cond_t *get_consumer_cond() {
        return &this->consumer;
    }
    pthread_cond_t *get_producer_cond() {
        return &this->producer;
    }
    int get_or_set_shutdown(int is_set, int set_num) {
        if (is_set == 1) {
            this->shutdown = set_num;      
            return this->shutdown;
        } else  {
            return this->shutdown; 
        }
    }
    
private:
    pthread_t manager_id;
    worker_thread *work_thread;
    size_t min_thread_num;
    size_t max_thread_num;
    size_t live_thread_num;
    size_t exit_thread_num;
    size_t busy_thread_num;
    size_t have_finish_work_num;
    size_t have_fail_work_num;
    /* If want to get or set mutex resource of thread pool,
     * must use this lock*/
    pthread_mutex_t thread_pool_mutex;
    pthread_mutex_t busy_thread_mutex;
    pthread_cond_t producer;
    pthread_cond_t consumer;
    int shutdown;
    ~thread_pool();
};

thread_pool::thread_pool(size_t min_thread_number, size_t max_thread_number, size_t list_max_number):
                         thread_task_list(list_max_number)
{
    printf("Come into %s\n", __func__);

    if (max_thread_number < min_thread_number) {
        cout << "Initial parameters error!\n";
        return;
    }

    printf("%s %d: >>>>>> Start creating thread pool <<<<<<\n", __func__, __LINE__);
    do {
        printf("%s %d: >>>>>> Start creating worker threads <<<<<<\n", __func__, __LINE__);
        try {
            this->work_thread = new worker_thread[max_thread_number];
            this->min_thread_num = min_thread_number;
            this->max_thread_num = max_thread_number;
            this->busy_thread_num = 0;
            this->live_thread_num = 0;
            this->exit_thread_num = 0;
            this->have_fail_work_num = 0;
            this->have_finish_work_num = 0;
            printf("%s %d: >>>>>> Successfully create worker threads <<<<<<\n", __func__, __LINE__);
        } catch(std::bad_alloc &e) {
            cout << "Fail to alloc for work threads, error info: " << e.what() << endl;
            work_thread = nullptr;
            break;
        }

        printf("%s %d: >>>>>> Start creating manager thread <<<<<<\n",
               __func__, __LINE__);
        if (pthread_create(&this->manager_id, nullptr, thread_manager, (void *)this)) {
            printf("%s %d: Fail to cretae manager thread\n", __func__, __LINE__);
            break;
        }
        printf("%s %d: >>>>>> Successfully create manager thread <<<<<<\n",
               __func__, __LINE__);

        printf("%s %d: >>>>>> Start to init conds and mutex locks <<<<<<\n",
               __func__, __LINE__);
        if (pthread_mutex_init(&this->thread_pool_mutex, nullptr)
            || pthread_mutex_init(&this->busy_thread_mutex, nullptr)
            || pthread_cond_init(&this->consumer, nullptr)) {
            cout << "Initiate lock failed" << endl;
            break;
        }
        printf("%s %d: >>>>>> Successfully init conds and mutex locks <<<<<<\n",
               __func__, __LINE__);

        this->shutdown = 0;

        printf("%s %d: >>>>>> Successfully create and initiate thread pool <<<<<<\n",
               __func__, __LINE__);
        return;
    } while (0);

    if (this->work_thread) {
        delete[] this->work_thread;
        this->work_thread = nullptr;
    }
    printf("%s %d: >>>>>> Fail to create and initiate thread pool <<<<<<\n",
           __func__, __LINE__);
    return;
}

thread_pool::~thread_pool()
{
    pthread_mutex_destroy(&this->thread_pool_mutex);
    pthread_mutex_destroy(&this->busy_thread_mutex);
    pthread_cond_destroy(&this->consumer);
    pthread_cond_destroy(&this->producer);
    /*Must check here, maybe it is deleted before.*/
    if (this->work_thread) {
        delete[] this->work_thread;
    }
}


void* thread_manager(void* arg)
{
    printf("%s %d: Come here\n", __func__, __LINE__);
    thread_pool *thread_pool = nullptr;
    worker_thread* work_thread = nullptr;
    if (arg == nullptr) {
        printf("%s %d: arg is null\n", __func__, __LINE__);
        pthread_exit(nullptr);
    }
    thread_pool = static_cast<class thread_pool*>(arg);

    while (1) {
        /*Check every five seconds*/
        sleep(5);

        pthread_mutex_lock(thread_pool->get_thread_pool_busy_thread_mutex());
        if (thread_pool->get_task_list_cur_size() == 0) {
            printf("%s %d: Have finish %lu tasks, fail %lu\n",__func__, __LINE__,
                   thread_pool->get_or_add_finish_task_num(0),
                   thread_pool->get_or_add_fail_task_num(0));
        }
        pthread_mutex_unlock(thread_pool->get_thread_pool_busy_thread_mutex());

        pthread_mutex_lock(thread_pool->get_thread_pool_mutex());
        printf("%s %d: Current task num size %lu, task capacity %lu, min thread num %lu, max thread num %lu\n",
               __func__, __LINE__, thread_pool->get_task_list_cur_size(),
               thread_pool->get_task_list_max_size(),
               thread_pool->get_min_thread_num(), thread_pool->get_max_thread_num());
        pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());

        if (thread_pool->get_or_set_shutdown(0, 0) == 1) {
            pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());
            break;
        }

        pthread_mutex_lock(thread_pool->get_thread_pool_busy_thread_mutex());
        int thread_busy_num = thread_pool->get_or_add_busy_thread_num(0);
        pthread_mutex_unlock(thread_pool->get_thread_pool_busy_thread_mutex());

        int thread_exit = (thread_pool->get_or_add_live_thread_num(0) - thread_busy_num) / 2;
        /* If current task size is bigger than thread_exit,
         * or equal to thread_exit, do not exit son thread;
         */
        thread_exit = thread_exit - thread_pool->get_task_list_cur_size();
        if (thread_exit > 0) {
            printf("%s %d: Current live thread num %lu, busy thread num %d, should exit thread num %d\n", __func__, __LINE__, thread_pool->get_or_add_live_thread_num(0), thread_busy_num, thread_exit);
            thread_pool->get_or_add_exit_thread_num(thread_exit);
            pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());
            pthread_cond_signal(thread_pool->get_consumer_cond());
            continue;
        }
        size_t thread_live = thread_pool->get_or_add_live_thread_num(0);
        size_t thread_task_size = thread_pool->get_task_list_cur_size();
        if (thread_task_size == 0) {
            pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());
            printf("%s %d: Thread task size is zero\n", __func__, __LINE__);
            continue;
        }
        size_t create_thread_num = thread_task_size - thread_live;
        size_t add_thread_max_num = thread_pool->get_max_thread_num() - thread_pool->get_or_add_live_thread_num(0);
        printf("%s %d: max_thread_num %ld, live_thread_num %ld, task_num %ld\n", __func__, __LINE__, thread_pool->get_max_thread_num(), thread_pool->get_or_add_live_thread_num(0), thread_pool->get_task_list_cur_size());
        create_thread_num = create_thread_num < add_thread_max_num ? create_thread_num : add_thread_max_num;
        printf("%s %d: create_thread_num %ld, add_thread_max_num %ld, create_thread_num %ld\n", __func__, __LINE__, create_thread_num, add_thread_max_num, create_thread_num);
        pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());

        work_thread_add(thread_pool, create_thread_num);
    }

    size_t i = 0;
    while (1) {
        pthread_mutex_lock(thread_pool->get_thread_pool_mutex());
        /*If tid != 0, need to recycle resource.*/
        if (thread_pool->get_or_add_live_thread_num(0) == 0) {
            printf("%s %d: Live thread num is zero, start to recycle\n", __func__, __LINE__);
            work_thread = thread_pool->get_worker_thread();
            for (; i < thread_pool->get_max_thread_num(); i++) {
                if (work_thread[i].get_thread_tid() != 0) {
                    printf("%s %d: Successfully recycle thread %ld\n",
                           __func__, __LINE__,work_thread[i].get_thread_tid());
                    pthread_join(work_thread[i].get_thread_tid(), nullptr);
                }
            }
            pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());
            break;
        } else {
            /*Wait for a little moment.*/
            printf("%s %d: Live thread num is %lu, wait for a moment\n",
                   __func__, __LINE__, thread_pool->get_or_add_live_thread_num(0));
            pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());
            /*must signal consumer*/
            pthread_cond_signal(thread_pool->get_consumer_cond());
            usleep(1);
        }
    }
    pthread_exit(nullptr);
}


worker_thread* find_thread_in_work_thread_list(worker_thread* work_thread,
                                               int list_size, pthread_t tid) {
    int i = 0;
    for (i = 0; i < list_size; i++) {
        if (pthread_equal(work_thread[i].get_thread_tid(), tid)) {
            return &work_thread[i];
        }
    }
    return nullptr;
}

void* thread_worker(void* arg)
{
    printf("%s %d: come here\n", __func__, __LINE__);
    thread_pool *thread_pool = nullptr;
    worker_thread *thread_self = nullptr;
    thread_pool_task thread_task;
    if (arg == nullptr) {
        printf("%s %d: thread pool is null\n", __func__, __LINE__);
        return nullptr;
    }
    thread_pool = static_cast<class thread_pool *>(arg);
    while (1) {
        pthread_mutex_lock(thread_pool->get_thread_pool_mutex());
        /*If thread pool shutdown, thread must exit.*/
        if (thread_pool->get_or_set_shutdown(0, 0) == 1) {
            printf("%s %d: Thread pool shutdown, thread %ld exits.\n",
                __func__, __LINE__, pthread_self());
            thread_pool->get_or_add_live_thread_num(-1);
            pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());
            pthread_exit(nullptr);
        }

        if (thread_pool->get_or_add_exit_thread_num(0)) {
            thread_pool->get_or_add_exit_thread_num(-1);
            /*
             * If live thread num is less than min_thread_num,
             * just decrease exit_thread_num, do not exit thread.
             */
            if (thread_pool->get_or_add_live_thread_num(0) > thread_pool->get_min_thread_num()) {
                thread_self = find_thread_in_work_thread_list(thread_pool->get_worker_thread(),
                              thread_pool->get_max_thread_num(), pthread_self());
                if (thread_self) {
                    printf("%s %d: Find thread %lu\n", __func__, __LINE__, pthread_self());
                    thread_self->set_use_status(0);
                } else {
                    printf("%s %d: Something error, cannot find pthread %lu in thread list\n",
                        __func__, __LINE__, pthread_self());
                }
                thread_pool->get_or_add_live_thread_num(-1);
                printf("%s %d: Thread %lu exits\n", __func__, __LINE__, pthread_self());
                pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());
                pthread_exit(nullptr);
            } else {
                printf("%s %d: min thread num is %lu, live thread num is %lu, thread %ld cannot exit.\n", __func__, __LINE__,
                       thread_pool->get_min_thread_num(), thread_pool->get_or_add_live_thread_num(0), pthread_self());
            }
        }
        
        /*If there is no task now, must wait*/
        if (thread_pool->get_task_list_cur_size() == 0) {
            pthread_cond_wait(thread_pool->get_consumer_cond(), thread_pool->get_thread_pool_mutex());
            /*Double check*/
            if (thread_pool->get_task_list_cur_size() == 0) {
                pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());
                continue;
            }
        }

        thread_pool_task *thread_task_p = thread_pool->task_get();
        if (!thread_task_p) {
            printf("%s %d: Thread task is null, continue.\n", __func__, __LINE__);
            pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());
            continue;
        }
        pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());

        thread_task = *thread_task_p;
        /*Check task parameter*/
        if (thread_task.task_param_check() == false) {
            printf("%s %d: Error, argument is null\n", __func__, __LINE__);
            continue;
        }

        pthread_mutex_lock(thread_pool->get_thread_pool_busy_thread_mutex());
        /*Add busy thread num*/
        thread_pool->get_or_add_busy_thread_num(1);
        pthread_mutex_unlock(thread_pool->get_thread_pool_busy_thread_mutex());

        printf("%s %d: ========== Thread %lu starts working ==========\n",
               __func__, __LINE__, pthread_self());

        thread_task.task_exec();
        pthread_cond_signal(thread_pool->get_producer_cond());

        pthread_mutex_lock(thread_pool->get_thread_pool_busy_thread_mutex());
        thread_pool->get_or_add_finish_task_num(1);
        pthread_mutex_unlock(thread_pool->get_thread_pool_busy_thread_mutex());

        thread_task.task_arg_delete();

        pthread_mutex_lock(thread_pool->get_thread_pool_busy_thread_mutex());
        thread_pool->get_or_add_busy_thread_num(-1);
        pthread_mutex_unlock(thread_pool->get_thread_pool_busy_thread_mutex());
        printf("%s %d: ========== Thread %ld ends working ============\n\n",
               __func__, __LINE__, pthread_self());
    }
    return nullptr;
}

int work_thread_add(thread_pool *thread_pool, size_t add_num)
{
    printf("%s %d: Come here\n", __func__, __LINE__);
    if (thread_pool == nullptr) {
        printf("%s %d: Thread pool is null\n", __func__, __LINE__);
        return false;
    }
    if (add_num <= 0) {
        printf("%s %d: Thread add num %ld is not bigger than zero\n", __func__, __LINE__, add_num);
        return false;
    }
    pthread_mutex_lock(thread_pool->get_thread_pool_mutex());

    if (thread_pool->get_or_set_shutdown(0, 0) == 1) {
        pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());
        printf("%s %d: Thread pool shutdown, cannot add new thread\n", __func__, __LINE__);
        return false;
    }

    worker_thread *work_thread = thread_pool->get_worker_thread();
    size_t i = 0;
    for (; i < thread_pool->get_max_thread_num(); i++) {
        /*If son thread has exited, manager must recycle its resource.*/
        if (work_thread[i].get_use_status() == 0 && work_thread[i].get_thread_tid()) {
            printf("%s %d: >>>>>> Start to recycle thread %lu\n",
                   __func__, __LINE__, work_thread[i].get_thread_tid());
            pthread_join(work_thread[i].get_thread_tid(), nullptr);
            printf("%s %d: Successfully recycle thread %lu <<<<<<\n",
                   __func__, __LINE__, work_thread[i].get_thread_tid());
            work_thread[i].set_thread_tid(0);
        }
        if ((add_num > 0) && (work_thread[i].get_thread_tid() == 0)) {
            if (pthread_create(work_thread[i].get_thread_tid_addr(), nullptr, thread_worker, (void*)thread_pool)) {
                printf("%s %d: Fail to create thread\n", __func__, __LINE__);
                work_thread[i].set_thread_tid(0);
                work_thread[i].set_use_status(0);
                continue;
            }
            printf("%s %d: Successfully create thread %ld\n", __func__, __LINE__, work_thread[i].get_thread_tid());
            work_thread[i].set_use_status(1);
            thread_pool->get_or_add_live_thread_num(1);
            add_num--;
        }
    }
    pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());

    if (add_num > 0) {
        printf("%s %d: Warning: Thread pool is full, no space for new %ld thread.\n",
               __func__, __LINE__, add_num);
        return false;
    }
    return true;
}

bool thread_task_add(thread_pool* thread_pool, int num)
{
    if (num <= 0) {
        printf("%s %d: Error task num %d\n", __func__, __LINE__, num);
        return false;
    }

    pthread_mutex_lock(thread_pool->get_thread_pool_mutex());
    printf("%s %d: thread pool live thread num %lu\n", __func__, __LINE__, thread_pool->get_or_add_live_thread_num(0));
    if (thread_pool->get_or_set_shutdown(0, 0) == 1) {
        printf("%s %d: Thread pool shutdown, cannot add new task\n", __func__, __LINE__);
        pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());
        return false;
    }

    if (thread_pool->get_task_list_cur_size() >= thread_pool->get_task_list_max_size()) {
        printf("%s %d: Task list is full, no space for new task\n",
               __func__, __LINE__);
        pthread_cond_wait(thread_pool->get_producer_cond(),
                          thread_pool->get_thread_pool_mutex());
    }

    int permit_num = thread_pool->get_task_list_max_size() - thread_pool->get_task_list_cur_size();
    num = num < permit_num ? num : permit_num;
    pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());

    int i = 0;
    for (;i < num; i++) {
        work_func work_function;
        switch (counter % 4) {
        case 0:
            work_function = func0;
            break;
        case 1:
            work_function = func1;
            break;
        case 2:
            work_function = func2;
            break;
        case 3:
            work_function = func3;
        }

        int* arg = nullptr;
        try {
            arg = new int;
        } catch (std::bad_alloc &e) {
            cout << "Fail to new for arg, error info: " << e.what() << endl;
            continue;
        }
        *arg = ++counter;
        thread_pool->task_add(work_function, static_cast<void *>(arg));
        pthread_cond_signal(thread_pool->get_consumer_cond());
    }
    return true;
}

int thread_pool_destroy(thread_pool* thread_pool)
{
    printf(">>>>>> Start to destory thread <<<<<<\n");
    if (thread_pool == nullptr) {
        printf("%s %d: Thread pool is null.\n", __func__, __LINE__);
        return false;
    }
    pthread_mutex_lock(thread_pool->get_thread_pool_mutex());
    thread_pool->get_or_set_shutdown(1, 1);
    pthread_t manager_id = thread_pool->get_manager_id();
    pthread_mutex_unlock(thread_pool->get_thread_pool_mutex());
    pthread_join(manager_id, nullptr);
    printf(">>>>>> Successfully to destory thread <<<<<<\n");
    return true;
}

typedef enum OPTYPE {
    CREATE_THREAD_POOL = 1,
    ADD_NEW_TASK,
    DESTORY_THREAD,
    CONTINUE,
    EXIT
} OPTYPE;

int main()
{
    unsigned int opcode;
    int min_thread_num;
    int max_thread_num;
    int task_list_capacity;
    int add_task_num;
    thread_pool *thread_pool = nullptr;
    unsigned int continue_time = 0;

    while(1) {
        sleep(continue_time);
        if (!thread_pool || (thread_pool && thread_pool->get_task_list_cur_size() == 0)) {
            printf("Please input your option:\
                   \ninput 1: create a new thread pool and add some task\
                   \ninput 2: add new task\
                   \ninput 3: destory thread pool\
                   \ninput 4: input continue time\
                   \ninput 5: exit\n=================\n");
            scanf("%u", &opcode);
        } else {
            continue;
        }
        switch(opcode) {
            case CREATE_THREAD_POOL:
                if (thread_pool) {
                    printf("%s %d: Error, thread pool is already created.\n", __func__, __LINE__);
                    break;
                }
                counter = 0;
                printf("Create thread pool\n");
  
                printf("Please input min work thread num: ");
                scanf("%u", &min_thread_num);
                printf("\n");
  
                printf("Please input max work thread num: ");
                scanf("%u", &max_thread_num);
                printf("\n");

                printf("Please input task list capacity: ");
                scanf("%u", &task_list_capacity);
                printf("\n");

                printf("Please input add task num after creating thread pool: ");
                scanf("%u", &add_task_num);
                printf("\n");

                try {
                    thread_pool = new class thread_pool(min_thread_num, max_thread_num,
                                                       task_list_capacity);
                } catch (std::bad_alloc &e) {
                    printf("%s %d: Fail to create thread pool, error info %s\n",
                           __func__, __LINE__, e.what());
                    break;
                }

                if (thread_pool && thread_task_add(thread_pool, add_task_num) == false) {
                    printf("%s %d: Fail to add new task\n", __func__, __LINE__);
                    break;
                }
                break;
            case ADD_NEW_TASK:
                if (thread_pool == nullptr) {
                    printf("%s %d: Fail to add new task, thread pool is null.\n",
                           __func__, __LINE__);
                    break;
                } else {
                    printf("add new task num: ");
                    scanf("%d", &add_task_num);
                    printf("\n");
                    if (thread_task_add(thread_pool, add_task_num) == false) {
                        printf("%s %d: Fail to add new task\n", __func__, __LINE__);
                        break;;
                    }
                }
                break;
            case DESTORY_THREAD:
                if (thread_pool && (thread_pool_destroy(thread_pool) == false)) {
                    printf("%s %d: Fail to destory thread pool\n", __func__, __LINE__);
                }
                thread_pool = nullptr;
                break;
            case CONTINUE:
                printf("Please input the continue seconds: ");
                scanf("%u", &continue_time);
                printf("\n");
                break;
            case EXIT:
                printf("Start to exit...\n");
                if (thread_pool && (thread_pool_destroy(thread_pool) == false )) {
                    printf("%s %d: Fail to destory thread pool\n", __func__, __LINE__);
                }
                printf("Successfully exit.\n");
                return 0;
            default:
                printf("Unknow opcode, please input again\n");
                break;
        }
    }
    return 0;
}
